package benzapp.db;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import com.google.android.maps.GeoPoint;

import benzapp.overlay.DistributoreOverlay;
import benzapp.pack.R;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import android.widget.Toast;

/**
 * classe che estende SQLiteOpenHelper e si occupa dell'interazione con il database
 * @author Michele Segata
 * */

public class DBHelper extends SQLiteOpenHelper {
	
	private final Context context;
	private SQLiteDatabase db;
	
	private SharedPreferences preferences;
	private static final double KM_LAT = 0.008983157;
	private float raggio;
	private int max_risultati;
	
	//dati del database
	public static final String TABLE = "distributori";
	public static final String ID = "_id";
	public static final String LATITUDINE = "latitudine";
	public static final String LONGITUDINE = "longitudine";
	public static final String DESCRIZIONE = "descrizione";
	public static final String BENZINA = "benzina";
	public static final String DIESEL = "diesel";
	public static final String GPL = "gpl";
	public static final String METANO = "metano";
	public static final String AGGIORNATO="aggiornato";
	public static final String DB_NAME = "distributori.db";
	public static final int DB_VERSION = 1;
	public static final String DB_PATH="data/data/benzapp.pack/databases/";
	public static final String [] allColumns={
		
		ID,LATITUDINE,LONGITUDINE,DESCRIZIONE,BENZINA,DIESEL,GPL,METANO,AGGIORNATO
		
	};
	
	
	public DBHelper(Context context, String name, CursorFactory factory, int version) {
		super(context, name, factory, version);
		this.context = context;
		
		preferences = context.getSharedPreferences("Opzioni", context.MODE_PRIVATE);
		raggio = preferences.getFloat("raggio", 1);
		max_risultati = preferences.getInt("max_risultati", 10);
		Log.d("raggio", raggio+"");
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		// TODO Auto-generated method stub

	}

	/**
	 * @author Michele Segata
	 * metodo che copia nella cartella /data/data/benzapp.pack/databases il db, partendo dai file 
	 * splittati presenti nella cartella res/raw
	 * @exception IOException se non riesce ad aprire i file
	 * */
	
	private void copyDataBase() throws IOException{
		 
		//apro stream di input e outpu
		OutputStream databaseOutputStream=new FileOutputStream("/data/data/benzapp.pack/databases/distributori.db");
		InputStream databaseInputStream;
				       
		//creo buffer per la copia
		byte[] buffer = new byte[1024];
		int length;
		      
		//prendo i file dalla cartella
		databaseInputStream =context.getResources().openRawResource(R.raw.distributoriaa);
			
		//li copio
		while ( (length = databaseInputStream.read(buffer)) > 0 ) {
			
				databaseOutputStream.write(buffer);
				
		}
		
		 databaseInputStream.close();
				       
		databaseInputStream =context.getResources().openRawResource(R.raw.distributoriab);
		
		while ( (length = databaseInputStream.read(buffer)) > 0 ) {
		
			databaseOutputStream.write(buffer);
			
		}
		
		//chiudo gli stream	
		databaseInputStream .close();        
		databaseOutputStream.flush();
		databaseOutputStream.close(); 
    }
	
	
	/**
	 * @author Michele Segata
	 * metodo che verfica se il db esiste o meno
	 * @return false se il db non è presente, true altrimenti
	 * */
	private boolean checkDataBase(){
		 
    	SQLiteDatabase checkDB = null;
 
    	try{
    		
    		//verifica se il db esiste provando ad aprirlo
    		String myPath = DB_PATH + DB_NAME;
    		checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READWRITE);
 
    	}catch(SQLiteException e){
 
    		//il database non esiste
    		Log.d("DB", "Il database non esiste");
 
    	}
 
    	if(checkDB != null){
 
    		//se esiste chiudo la connessione al db e ritorna true, false altrimenti
    		checkDB.close();
 
    	}
 
    	return checkDB != null ? true : false;
    }
	
	/**
	 * @author Michele Segata
	 * metodo che crea il database se questo non è già presente nei file del dispositivo
	 * @throws IOException se si verifica un errore nella copia del database
	 * */
	
	public void createDataBase() throws IOException {
		 
		//controlla se il database esiste
    	boolean dbExist = checkDataBase();
 
    	//se non esiste lo copia dai file presenti nella cartella raw
    	if(!dbExist){
    		 
    		//crea un db vuoto dentro cui creare quello esistente
        	this.getWritableDatabase();
 
        	try {
 
    			copyDataBase();
 
    		} catch (IOException e) {
 
    			Log.e("DB","Errore nella copia del db");
        		throw new Error("Error copying database"); 
        	}
    	}
 
    }
	
	/**
	 * @author Michele Segata
	 * Metodo che apre il database in lettura e scrittura
	 * @throws SQLException
	 * */	
	public void openDataBase() throws SQLException{
		 
    	//apro il database
        String dbPath = DB_PATH + DB_NAME;
    	db = SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READWRITE);
 
    }
	
	/**
	 * @author Valentino Briani
	 * Metodo che chiude il database
	 * @throws SQLException
	 */
	public void closeDataBase() throws SQLException{
		if (db.isOpen())
			db.close();
    }
	
	
		/**
		 * metodo che converte il Cursor con cui viene fatta la query nel database in una istanza della classe Distributore
		 * @author Michele Segata
		 * @param il Cursor da convertire in Distributore
		 * @return l'istanza della classe Distributore
		 * */
	public Distributore cursorToDistributore(Cursor c) {
			
			//mi sposto all'inizio del cursore
			c.moveToFirst();
			int id = c.getInt(0);
			double longitudine = c.getDouble(1);
			double latitudine = c.getDouble(2);
			String descrizione = c.getString(3);
			Float benzina = c.getFloat(4);
			Float gpl = c.getFloat(5);
			Float diesel = c.getFloat(6);
			Float metano = c.getFloat(7);
			String aggiornamento = c.getString(8);
			Distributore d = new Distributore(id, latitudine, longitudine, descrizione, benzina, diesel, gpl, metano, aggiornamento);
			Log.d("DB", "Distributore "+id+" creato correttamente");
			//chiudo il cursore prima di restituire l'istanza di Distributore
			c.close();
			return d;
		}
		
				
		/**
		 * Metodo che restituisce i distributori nel raggio e nel limite di risultati decisi dall'utente 
		 * @author Michele Segata, Valentino Briani
		 * @param la posizione corrente
		 * @return ArrayList dei distributori
		 * */
		public ArrayList<DistributoreOverlay> getNearestOveralys(GeoPoint posizione) {
			
			ArrayList<DistributoreOverlay> list = new ArrayList<DistributoreOverlay>();
			Log.d("Lista query", "Arraylist creato");
			
			//prende i distributori non più lontani di tot
			
			Log.d("STAMPA", String.valueOf(posizione.getLatitudeE6()/1E6));
			//FORMULA DISTANZA= ((ACOS(SIN(lat1 * PI() / 180) * SIN(lat2 * PI() / 180) + COS(lat1 * PI() / 180) * COS(lat2 * PI() / 180) * COS((long1 - long2) * PI() / 180)) * 180 / PI()) * 60 * 1.1515 * 1.609344)
			//String query="SELECT * FROM distributori WHERE latitudine between ("+currentLocation.getLatitudeE6()/1E6+"- 0.008983) and ( "+currentLocation.getLatitudeE6()/1E6+"+ 0.008983) and longitudine between ("+currentLocation.getLongitudeE6()/1E6+"- 0.008983) and ( "+currentLocation.getLongitudeE6()/1E6+"+ 0.008983)";
			
			/* FUNZIONANTE
			String query="SELECT * FROM distributori WHERE latitudine between ("+currentLocation.getLatitudeE6()/1E6+"- 0.008983) and ( "+currentLocation.getLatitudeE6()/1E6+"+ 0.008983) and longitudine between ("+currentLocation.getLongitudeE6()/1E6+"- 0.008983) and ( "+currentLocation.getLongitudeE6()/1E6+"+ 0.008983)";*/
			
			final double KM_LONG = 360/(Math.cos(Math.toRadians(posizione.getLatitudeE6()/1E6))*40075);
			
			String query = "SELECT * " +
					"FROM distributori " +
					"WHERE latitudine between (" +
					(posizione.getLatitudeE6()/1E6 - raggio*KM_LAT) + ") and (" +
					(posizione.getLatitudeE6()/1E6 + raggio*KM_LAT) +
					") and longitudine between (" +
					(posizione.getLongitudeE6()/1E6 - raggio*KM_LONG) + ") and (" +
					(posizione.getLongitudeE6()/1E6 + raggio*KM_LONG) + ") " +
					"LIMIT " + max_risultati;
			
			Cursor c = db.rawQuery(query, null);
			Log.d("Query",query);
			Log.d("QUERY", "query effettuata");
			
			//c.moveToFirst();
			Distributore d;
			DistributoreOverlay o;
							
			//se il cursore non è vuoto popolo lista
			if(c.moveToFirst()) {				
				while(!c.isAfterLast()){					
					int id = c.getInt(0);
					double longitudine = c.getDouble(1);
					double latitudine = c.getDouble(2);
					String descrizione = c.getString(3);
					Float benzina = c.getFloat(4);
					Float gpl = c.getFloat(5);
					Float diesel = c.getFloat(6);
					Float metano = c.getFloat(7);
					String aggiornamento = c.getString(8);
					d = new Distributore(id, latitudine, longitudine, descrizione, benzina, diesel, gpl, metano, aggiornamento);				
					Log.d("Overlay", "Creato distributore "+d.toString());
					//o=new DistributoreOverlay(new GeoPoint((int)(d.getLatitudine()/1E6),(int) (d.getLongitudine()/1E6)), d.getDescrizione(), d.getDescrizione(),context,d);
					o = new DistributoreOverlay(new GeoPoint((int)(d.getLatitudine()*1E6),(int) (d.getLongitudine()*1E6)), d.getDescrizione(), d.getDescrizione() ,context, d);
					list.add(o);	
					Log.d("Overlay","Aggiunto overlay "+o.getSnippet());
					
					c.moveToNext();
				}				
			}
			
			if(list.size()>0){
				Toast.makeText(context, "Distributori trovati: " + list.size(), Toast.LENGTH_SHORT).show();			
			} else {
				Toast.makeText(context, "Nessun distributore trovato!", Toast.LENGTH_SHORT).show();
			}
			
			//chiudo cursore e ritorno la lista
			c.close();
			
			return list;
			
			
		}
		
		
		/**
		 * @author Valentino Briani
		 * @param distributore
		 * @throws SQLiteException
		 * Metodo che aggiorna i prezzi di un distributore nel db
		 */
		public void aggiornaDistributore(Distributore distributore) throws SQLiteException {
			Log.d("DBHelper", "Aggiornamento distributore: " + distributore.getDescrizione());
			//creo ContentValues e inserisco valori 
			ContentValues values = new ContentValues();
			values.put(BENZINA, distributore.getBenzina());
			values.put(DIESEL, distributore.getDiesel());
			values.put(GPL, distributore.getGpl());
			values.put(METANO, distributore.getMetano());
			values.put(AGGIORNATO, distributore.getAggiornamento());
			//aggiorno distributore con id=Distributore.getId; update ritorna il numero di distributori aggiornati
			int aggiornati = db.update(TABLE, values, ID + "=" + distributore.getId(), null);
			if (aggiornati>1) {
				//più di un distributore aggiornato
				Log.e("DBHelper", "Errore: più di un distributore aggiornato");
				throw new SQLiteException();
			} else if (aggiornati<1) {
				//nessun distributore aggiornato
				Log.e("DBHelper", "Errore: nessun distributore aggiornato");
				throw new SQLiteException();
			} else {
				//aggiornamento corretto
				Log.d("DBHelper", "Distributore aggiornato correttamente: " + distributore.getDescrizione());
			}
		}
		
}
